function mxPanningHandler(graph, factoryMethod)
{
	if (graph != null && document.body != null)
	{
		this.graph = graph;
		this.factoryMethod = factoryMethod;
		this.graph.addMouseListener(this);
	}
};
mxPanningHandler.prototype = new mxPopupMenu();
mxPanningHandler.prototype.constructor = mxPanningHandler;
mxPanningHandler.prototype.graph = null;
mxPanningHandler.prototype.usePopupTrigger = true;
mxPanningHandler.prototype.useLeftButtonForPanning = false;
mxPanningHandler.prototype.selectOnPopup = true;
mxPanningHandler.prototype.clearSelectionOnBackground = true;
mxPanningHandler.prototype.ignoreCell = false;
mxPanningHandler.prototype.useGrid = false;
mxPanningHandler.prototype.panningEnabled = true;
mxPanningHandler.prototype.isPanningEnabled = function()
{
	return this.panningEnabled;
};
mxPanningHandler.prototype.setPanningEnabled = function(value)
{
	this.panningEnabled = value;
};
mxPanningHandler.prototype.init = function()
{
	mxPopupMenu.prototype.init.apply(this);

	var self = this;
	mxEvent.addListener(this.div, 'mousemove', function(evt)
	{
		self.graph.tooltipHandler.hide();
	});
};
mxPanningHandler.prototype.isPanningTrigger = function(me)
{
	var evt = me.getEvent();
	return (this.useLeftButtonForPanning && (this.ignoreCell || me.getState() == null)
		&& mxEvent.isLeftMouseButton(evt)) || (mxEvent.isControlDown(evt) && mxEvent.isShiftDown(evt))
		|| (this.usePopupTrigger && mxEvent.isPopupTrigger(evt));
};
mxPanningHandler.prototype.mouseDown = function(sender, me)
{
	if (!me.isConsumed() && this.isEnabled())
	{
		this.hideMenu();
		this.dx0 = -this.graph.container.scrollLeft;
		this.dy0 = -this.graph.container.scrollTop;
		this.popupTrigger = this.isPopupTrigger(me);
		this.panningTrigger = this.isPanningEnabled() && this.isPanningTrigger(me);
		this.startX = me.getX();
		this.startY = me.getY();

		if (this.popupTrigger && mxEvent.isRightMouseButton(me.getEvent()) && mxClient.IS_MAC && mxClient.IS_SF)
		{
			this.mouseUp(sender, me);
		}
		else if (this.panningTrigger)
		{
			me.consume();
		}
	}
};
mxPanningHandler.prototype.mouseMove = function(sender, me)
{
	var dx = me.getX() - this.startX;
	var dy = me.getY() - this.startY;

	if (this.active)
	{
		if (this.useGrid)
		{
			dx = this.graph.snap(dx);
			dy = this.graph.snap(dy);
		}
		this.graph.panGraph(dx + this.dx0, dy + this.dy0);
		me.consume();
	}
	else if (this.panningTrigger)
	{

		this.active = Math.abs(dx) > this.graph.tolerance || Math.abs(dy) > this.graph.tolerance;
	}
};
mxPanningHandler.prototype.mouseUp = function(sender, me)
{
	var dx = Math.abs(me.getX() - this.startX);
	var dy = Math.abs(me.getY() - this.startY);

	if (this.active)
	{
		var style = mxUtils.getCurrentStyle(this.graph.container);

		if (!mxUtils.hasScrollbars(this.graph.container))
		{
			this.graph.panGraph(0, 0);
			var dx = me.getX() - this.startX;
			var dy = me.getY() - this.startY;
			var scale = this.graph.getView().scale;
			var t = this.graph.getView().translate;
			this.panGraph(t.x + dx / scale, t.y + dy / scale);
		}
		me.consume();
	}
	else if (this.popupTrigger)
	{
		if (dx < this.graph.tolerance && dy < this.graph.tolerance)
		{
			var cell = me.getCell();

			if (this.graph.isEnabled() && this.selectOnPopup && cell != null && !this.graph.isCellSelected(cell))
			{
				this.graph.setSelectionCell(cell);
			}

			if (this.clearSelectionOnBackground && !this.graph.isCellSelected(cell))
			{
				this.graph.clearSelection();
			}
			this.graph.tooltipHandler.hide();
			var origin = mxUtils.getScrollOrigin();
			var point = new mxPoint(me.getX() + origin.x, me.getY() + origin.y);
			this.popup(point.x, point.y, cell, me.getEvent());
			me.consume();
		}
	}
	this.panningTrigger = false;
	this.popupTrigger = false;
	this.active = false;
};
mxPanningHandler.prototype.panGraph = function(dx, dy)
{
	this.graph.getView().setTranslate(dx, dy);
};
mxPanningHandler.prototype.destroy = function()
{
	this.graph.removeMouseListener(this);
	mxPopupMenu.prototype.destroy.apply(this);
};